interface-ip: fix subnet route handling
authorFelix Fietkau <[email protected]>
Tue, 15 Dec 2015 10:57:48 +0000 (11:57 +0100)
committerFelix Fietkau <[email protected]>
Tue, 15 Dec 2015 10:57:52 +0000 (11:57 +0100)
When the kernel subnet route has to be replaced, the cleanup call needs
to match the properties of the replacement route exactly, mainly the
metric and the routing table.
Fix handling this by embedding the device_route for the subnet in the
device_addr struct and using it in the cleanup path.

This fixes issues on config reload with changes to the routing table

Signed-off-by: Felix Fietkau <[email protected]>
interface-ip.c
interface-ip.h

index 220f4a01a25ae0702fe0142b972fe647926632a1..553361512095dc991d6c31fdfe036801afd1c5c3 100644 (file)
@@ -440,32 +440,37 @@ static void
 interface_handle_subnet_route(struct interface *iface, struct device_addr *addr, bool add)
 {
        struct device *dev = iface->l3_dev.dev;
-       struct device_route route;
        bool v6 = ((addr->flags & DEVADDR_FAMILY) == DEVADDR_INET6);
+       struct device_route *r = &addr->subnet;
 
        if (addr->flags & DEVADDR_OFFLINK)
                return;
 
-       memset(&route, 0, sizeof(route));
-       route.iface = iface;
-       route.flags = addr->flags;
-       route.mask = addr->mask;
-       memcpy(&route.addr, &addr->addr, sizeof(route.addr));
-       clear_if_addr(&route.addr, route.mask);
-
-       if (add) {
-               route.flags |= DEVADDR_KERNEL;
-               system_del_route(dev, &route);
+       if (!add) {
+               if (!addr->subnet.iface)
+                       return;
 
-               route.flags &= ~DEVADDR_KERNEL;
-               route.metric = iface->metric;
-               route.table = (v6) ? iface->ip6table : iface->ip4table;
-               if (route.table)
-                       route.flags |= DEVROUTE_SRCTABLE;
-               system_add_route(dev, &route);
-       } else {
-               system_del_route(dev, &route);
+               system_del_route(dev, r);
+               memset(r, 0, sizeof(*r));
+               return;
        }
+
+       r->iface = iface;
+       r->flags = addr->flags;
+       r->mask = addr->mask;
+       memcpy(&r->addr, &addr->addr, sizeof(r->addr));
+       clear_if_addr(&r->addr, r->mask);
+
+       r->flags |= DEVADDR_KERNEL;
+       system_del_route(dev, r);
+
+       r->flags &= ~DEVADDR_KERNEL;
+       r->metric = iface->metric;
+       r->table = (v6) ? iface->ip6table : iface->ip4table;
+       if (r->table)
+               r->flags |= DEVROUTE_SRCTABLE;
+
+       system_add_route(dev, r);
 }
 
 static void
@@ -1230,10 +1235,11 @@ void interface_ip_set_enabled(struct interface_ip_settings *ip, bool enabled)
 
                if (enabled) {
                        system_add_address(dev, addr);
-                       if (iface->metric)
-                               interface_handle_subnet_route(iface, addr, true);
 
                        addr->policy_table = (v6) ? iface->ip6table : iface->ip4table;
+                       if (iface->metric || addr->policy_table)
+                               interface_handle_subnet_route(iface, addr, true);
+
                        if (addr->policy_table)
                                set_ip_source_policy(true, v6, IPRULE_PRIORITY_ADDR, &addr->addr,
                                                (v6) ? 128 : 32, addr->policy_table, NULL, NULL);
index f24b0ec38fef04932aeb2631e67fb31637c7c374..bbef62ceb305f5f82ba8c1ffa2591ba39a92b3bb 100644 (file)
@@ -110,6 +110,8 @@ struct device_addr {
        bool failed;
        unsigned int policy_table;
 
+       struct device_route subnet;
+
        /* ipv4 only */
        uint32_t broadcast;
        uint32_t point_to_point;